import { Button } from "antd"

export default () => {
  const ecma1 = () => {
    var p1 = new Proxy({},{
      get: function(target,propkey,receiver) {
        console.log(target,propkey,receiver)
        return Reflect.get(target,propkey,receiver)
      },
      set: function(target,propkey,value,receiver) {
        console.log(target,propkey,value,receiver)
        return Reflect.set(target,propkey,value,receiver)
      }
    })
    p1.y = 88
    p1.y
    //target 原始对象，receiver自己，

    var handler = {
      get:function(target,propkey) {
        if(propkey === 'prototype') {
          console.log('call x.prototype')
          return Object.prototype;
        }
        return 'hello' + '-' + propkey
      },
      construct: function(target,args) {
        console.log('new object'+args.toString)
        return {value:args[1]}
      },
      apply: function(target, thisBinding, args) {
        console.log('call apply ')
        return args[0]
      }
    }

    var p2 = new Proxy(function(x,y){return x + y},handler)
    //normal:调用方法
    p2(3,4)//call apply

    //创建对象：
    new p2(3,4)//new objectfunction toString() { [native code] }


    //get
    p2.prototype === Object.prototype//prototype
    console.log(p2.callget) //callget


    //总结
    //--- 代理的本质就是，系统调用方法先经过我这个中间人同意，并且中间人想怎么办就怎么办，
    //--- 好处:不让外部感知内部对象。
    //--- 返回的可以假设是原来类型一模一样的一个玩意：can be used in place of the original object


  }
  const ecma2 = ()=> {
    //Reflect:将Object对象的一些明显属于语言内部的方法
    //改善Object方法的返回结果，更合理。两者都能用效果差不多，只不过Reflect更合理
    //--- 个人理解，就是提供一个工具类，操作对象
    //--- Reflect的方法 === Proxy的方法
    //--- Reflect总可以获取默认行为，Proxy的改变做的改动会失效
    //define
    var old1 = {}
    Object.defineProperty(old1,'param',{
      value:3
    })

    var new1 = {}
    Reflect.defineProperty(new1,'param',{
      value:5
    })
    console.log(old1,new1)//{param:3} {param:5}

    //has
    console.log('param' in old1)//true
    console.log(Reflect.has(new1,'param'))//true
  }
  const ecma3 = () => {
    //正常用法
    function *generator1() {
        yield 'hello'
        yield 'world'
        return 'ending'
    }
    var g1 = generator1()
    console.log(g1.next())//{value:'hello',done:false}
    console.log(g1.next())//{value:'world',done:false}
    console.log(g1.next())//{value:'ending',done:true}
    console.log(g1.next())//{value:'undefined',done:true}

    //next:表示上一个yield表达式的返回值,解决yield表达式天生没有返回值的弊端。
    function *generator2() {
      var x = yield 'hello'
      var y = yield 'world'
      //yield ：yield 后面不跟值，就返回undefined
      return {x,y}
  }
  var g2 = generator2()
  // console.log(g2.next())//{value:'hello',done:false}
  // console.log(g2.next())//{value:'world',done:false}
  // console.log(g2.next())//{value:{x:undefined,y:indefined},done:true}
  // console.log(g2.next())//{value:'undefined',done:true}
  console.log(g2.next())//{value:'hello',done:false}
  console.log(g2.next('hello'))//{value:'world',done:false}
  console.log(g2.next('world'))//{value:{x:'hello',y:'world'},done:true}
  console.log(g2.next())//{value:'undefined',done:true}


  //iterator
  var it_obj = {}
  it_obj[Symbol.iterator] = function* (){
    yield 3;
    yield 4;
  };
  console.log([...it_obj]) //展开对象



  //for of 1
  function * fbnq(){
    let [pre,curr] = [0,1]
    for(;;) {
      yield curr;
      [pre,curr] = [curr,pre+curr]
    }
  }
  for(let n of fbnq()) {
    if (n > 1000) {break}
    console.log(n)
  }
  //for of 1 2

  function *iterObj(obj) {
    let propKeys = Reflect.ownKeys(obj)
    for(let key of propKeys) {
      yield[key,obj[key]];
    }
  }

  let jane = {f:'f',s:'s'}
  for(let [key,value] of iterObj(jane)) {
    console.log(key,value)
  }

  //throw
  function* generator3() {
    try{
      yield;
    }catch(e){
      console.log(e)
    }
  }
  var g3 = generator3()
  g3.next()//执行到yield那了

  g3.throw('a')//注音：必须至少执行过一次next方法

  //return 提前终止
  
  function* generator4() {
    yield 1
    yield 2
    yield 3
  }
  var g4 = generator4()
  console.log(g4.next())
  console.log(g4.return())
  console.log(g4.next())
  
  //yield *:gen 调用 gen,代替for of。数据结构只要有 Iterator 接口，就可以被yield*遍历

  function* generatorTop(){
    yield 'a'
    yield 'b'
  }
  function* generatorBottom() {
    yield 'x'
    for(let c of generatorTop()) {
      console.log(c)
    }
    yield 'y'
  }
  function* generatorBottom_simple() {
    yield 'x'
    yield * generatorTop()
    yield 'y'
  }

  for(let n of generatorBottom()) {
    console.log(n)
  }
  for(let n of generatorBottom_simple()) {
    console.log(n)
  }

  //重大：应用
  //一 异步同步化:gen版本，太麻烦了，需要我一步一步执行

  function* loadUI() {
    console.log('create UI')
    const data = yield new Promise(resolve => {
      setTimeout(() => {
        resolve({'data':{'a':3}})
      }, 2000);
    })
    console.log('refresh UI',data)
  }
  var l = loadUI()
  l.next().value.then(function(data){
    l.next(data)
  })
  //二：多个同步步骤：一步一步执行
  //伪代码
  // yield step1()
  // yield step2()

  //或者
  
  //三：对象 for of 遍历对象

  

    //总结
    //--- next返回的是yield表达式的值
    //--- 实际上是一个迭代器对象，每次指针都指向函数头部or yield的地方
    //--- 重要： x = yield 3，但是 yield 3是没有返回值的 等同于 x = undefined,想要解决就得 next
    //--- next:把参数值带入 上次执行到的地方。相当于yield 表达式式返回了一个值
    //--- yield 后面不跟值，就返回undefined
    //--- yield一定要加分号
    //--- Generator 作用是create一个 遍历器
    //--- next throw return 都是替换yiled
    //Generator 函数也不能跟new命令一起用

    //彻底搞懂async 语法糖: https://www.jianshu.com/p/1c9e9c161612
    //

  }
  const ecma4 = ()=> {
    //异步四种
    //第一种 回调方法 x.get(url,{data -> in }):问题如下，多重请求
    //x.get(url,{data -> in 
    //    x.get(url,{data -> in })
    // })


    // 第二种 promise: 
    // x.get(url).then(function(){

    // })
    // .then(
    //   dosomething()
    // ).then(
    //   return x.get(url)
    // ).then(

    // )

    //generator
    // function *readfiles() {
    //   var f1 = yield readfiles()
    //   var f2 = yield readfiles(f1)
    // }
    // var r = readfiles()
    // var f = r.next()
    // var f2 = r.next(f1)


    // Thunk 函数的定义，它是“传名调用”

    //JS是传值调用，所以得自己实现“传名调用”，就是Thunk，生产环境 Thunkify。最大的好处：可以自动执行 Generator 函数

    //co 模块:可以自动执行 Generator 函数

    //Async Generator语法糖
    //--- 不需要执行器了。内置执行器。
    //--- 返回值是 Promise

    // 回调函数
    // 事件监听
    // 发布/订阅
    // Promise 对象？？？

   
  }

  const ecma5 = () => {
    //1.调度中心
    let Emitter = {}
    //1.1调度中心的容器: key（event）:vale([function])，一个event多个执行函数
    Emitter.observers = {}

    //1.2 注册进来，将observer存储到 容器中
    Object.defineProperty(Emitter,'on',{
      value:function(event,fn) {
        (this.observers[event]|| (this.observers[event] = [])).push(fn)
        return this
      }
    })
    // Emitter.on = function(event,fn) {
    //   var that = this
    //   (that.observers[event]|| (that.observers[event] = [])).push(fn)
    //   return that
    // }
    //1.3 发布
    Emitter.emit = function() {//可以n个参数
      console.log(this)
      let that = this
      //获取参数
      let event = Array.prototype.shift.call(arguments)

      //拿到函数
      let fns = that.observers[event]

      //执行函数

      fns.forEach(fn=> {
        fn.apply(that,arguments)
      })
      return that

    }

    //demo调用
    function callBack1(content) {
      console.log('call',content)
    }
    function callBack2(content) {
      console.log('call2')
    }
    Emitter.on('event1',callBack1)
    Emitter.on('event1',callBack2)

    Emitter.emit('event1','title content')

    //once:本质：包装，将包装添加到 observers。执行的时候执行wrapper，然后再移除wrraper，再执行fn
    function once(event,fn){


      //包装函数
      function wrapper() {
        //从全局容器 移除
        off(event,wrapper)
        fn.apply()
      }
      //包装
      wrapper.on = fn
      //添加到全局容器
      on(event,wrapper)
    }

    //总结
    //--- 观察者区别：搞了一个中间层，不让订阅者，和发生者直接接触。
    //--- 可以先发布，后订阅。

    //发布订阅模式+观察者模式:https://juejin.cn/post/7055441354054172709
    //https://juejin.cn/post/6844903850105634824


  }
  return (
    <div>
      <Button onClick={ecma1}>Proxy</Button>
      <Button onClick={ecma2}>Reflect</Button>
      <Button onClick={ecma3}>Generator</Button>
      <Button onClick={ecma4}>Generator异步</Button>
      <Button onClick={ecma5}>发布订阅模式</Button>
    </div>
  )
}